Skip to content

feat: add GetNoteError endpoint in ntx builder#1792

Merged
Mirko-von-Leipzig merged 4 commits intonextfrom
santiagopittella-get-note-error-ntx-builder
Mar 17, 2026
Merged

feat: add GetNoteError endpoint in ntx builder#1792
Mirko-von-Leipzig merged 4 commits intonextfrom
santiagopittella-get-note-error-ntx-builder

Conversation

@SantiagoPittella
Copy link
Collaborator

closes #1758

  • Add a GetNoteError gRPC endpoint that returns the latest execution error for a network note, including attempt count and last attempt block number.
  • The NTX builder now runs its own internal gRPC server. The RPC component proxies GetNoteError requests to it.
  • Store last_error and last_attempt_block_num for each note in the NTX builder database.

Comment on lines +445 to +457
per_note
.into_iter()
.map(|f| {
let note_error = f.error.as_report();
tracing::info!(
note.id = %f.note.id(),
nullifier = %f.note.nullifier(),
err = %note_error,
"note failed: consumability check",
);
(f.note.nullifier(), note_error)
})
.collect()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: might be worth deduping this and its use above. Maybe not though

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The match statement is pretty long in general. If we could reduce it that would be good.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed the dedup in 25aee9d

The match is still present, but it doesn't look as large now, we can still try to reduce it further tho.

async fn mark_notes_failed(&self, nullifiers: &[Nullifier], block_num: BlockNumber) {
async fn mark_notes_failed(
&self,
failed_notes: &[(Nullifier, String)],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would maybe be nice to replace String with Box<dyn ErrorReport> but the problem is that one of the branches doesn't provide an Error: (note.nullifier(), error_msg.clone()).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's doable, but instead of using Box, we need to wrap with Arc, and also restrain to Sync + Send, creating the following type:

type NoteError = Arc<dyn ErrorReport + Send + Sync>

Which may be an overcomplicated solution just to get rid of the String, so we can roll it back if prefered.

25aee9d

//
// This is useful for debugging notes that are failing to be consumed by
// the network transaction builder.
rpc GetNoteError(note.NoteId) returns (GetNoteErrorResponse) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its starting to feel like we are implementing a manual gateway/reverse proxy. But maybe thats not a bad thing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the RPC proxy into the "internal" components? It is essentially a gateway yeah.. If this was non gRPC we could probably use some off the shelf implementation for this, though maybe not, because there are some non-trivial validation steps happening.


## gRPC Server

The NTB exposes an internal gRPC server for querying its state. The RPC component proxies public
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: NTX Builder, not NTB

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 25aee9d

@juan518munoz
Copy link
Collaborator

juan518munoz commented Mar 16, 2026

With Santiago being out of office for the duration of this week, I'm taking over any remaining work he has on the node.

@sergerad @Mirko-von-Leipzig

@Mirko-von-Leipzig Mirko-von-Leipzig merged commit 4e7163c into next Mar 17, 2026
19 checks passed
@Mirko-von-Leipzig Mirko-von-Leipzig deleted the santiagopittella-get-note-error-ntx-builder branch March 17, 2026 13:38
Comment on lines +10 to +17
// API for querying network transaction builder state.
service Api {
// Returns the latest execution error for a network note, if any.
//
// This is useful for debugging notes that are failing to be consumed by
// the network transaction builder.
rpc GetNoteError(note.NoteId) returns (GetNoteErrorResponse) {}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be good to generalize this to something like GetNoteStatus. This way, we can return status of successfully executed notes as well. Otherwise, it may not be clear if the note just had a transient error and then was consumed correctly vs. the error is still preventing it from being consumed.

This could be something like GetNetworkNoteStatus endpoint, and it could return:

  • "Not found" if the note doesn't exist or is not a network note.
  • Status of the note - maybe:
    • pending - we are still trying to execute this note.
    • processed - the note made it into a transaction which was sent to the block producer.
    • committed - the network is now in the store
    • discarded - the note failed too many times and won't be retried in the future.
  • The last error associated with the note execution - basically, what we have here now (this is applicable for all status above).

I'll create an issue for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose network note failures

5 participants